#include "D20CharacterModel.h"
#include "D20CharacterItem.h"


#include <Item.h>
#include <ItemRoot.h>
#include <ItemReference.h>


#include <QStringList>
#include <QDebug>


D20CharacterModel::D20CharacterModel(QObject *parent) :
    D20BaseModel(parent)
{
    addGroup(HitDie,        QT_TR_NOOP("Hit Die"));
    addGroup(SkillPoints,   QT_TR_NOOP("Skill Points"));
    addGroup(ClassSkills,   QT_TR_NOOP("Class Skills"));
    addGroup(SavingThrows,  QT_TR_NOOP("Saving Throws"));
    addGroup(BaseBonuses,   QT_TR_NOOP("Base Bonuses"));
    addGroup(SpecialAbilities, QT_TR_NOOP("Special Abilities"));
}


void D20CharacterModel::setCampaignModel(D20CampaignModel* campaign)
{
    if (m_campaignModel.data() == campaign) return;

    m_campaignModel = campaign;

    rootItem()->setData(D20CharacterItem::CampaignData, m_campaignModel->rootItem()->filePath());

    QStringList names = m_campaignModel->skills(true);
    initializeGroup(ClassSkills, names);
    validateCheckable(ClassSkills, names);

    names = m_campaignModel->specials();
    initializeGroup(SpecialAbilities, names);
    validateCheckable(SpecialAbilities, names);

    names = m_campaignModel->baseBonuses();
    initializeGroup(BaseBonuses, names);

    names = m_campaignModel->saveBonuses();
    initializeGroup(SavingThrows, names);

}



QVariant D20CharacterModel::data(const QModelIndex &index, int role) const
{
    if (index.column() != Item::ValueData) return D20BaseModel::data(index, role);

    AbstractItem* item = indexItem(index);
    if (!item || IS_REFERENCE(item)) return D20BaseModel::data(index, role);

    QVariant id = item->data(Item::IdData, Qt::EditRole);
    QVariant value = item->data(Item::ValueData, Qt::EditRole);

    if (id.toString().contains(groupName(ClassSkills)))
    {
        if (id.toString().endsWith(groupName(ClassSkills))) return D20BaseModel::data(index, role);
        if (role != Qt::CheckStateRole) return QVariant();
        if (!value.isValid()) value = Qt::Unchecked;

        return value;
    }


    if (id.toString().contains(groupName(SpecialAbilities)))
    {
        if (id.toString().endsWith(groupName(SpecialAbilities))) return D20BaseModel::data(index, role);
        if (role != Qt::CheckStateRole) return QVariant();
        if (!value.isValid()) value = Qt::Unchecked;

        return value;
    }


    return value;

}


bool D20CharacterModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (index.column() != Item::ValueData) return ItemModel::setData(index, value, role);

    AbstractItem* item = indexItem(index);
    if (!item || IS_ROOT(item) || IS_REFERENCE(item)) return ItemModel::setData(index, value, role);


    QVariant id = item->data(AbstractItem::IdData, Qt::EditRole);

    if (id.toString().endsWith(groupName(HitDie)))
    {
        item->setData(Item::ValueData, value);
        emit dataChanged(index, index);
        return true;
    }

    if (id.toString().contains(groupName(ClassSkills)))
    {
        if (id.toString().endsWith(groupName(ClassSkills))) return false;
    }

    if (id.toString().contains(groupName(SpecialAbilities)))
    {
        if (id.toString().endsWith(groupName(SpecialAbilities))) return false;
    }

    item->setData(Item::ValueData, value);
    emit dataChanged(index, index);
    return true;
}


Qt::ItemFlags D20CharacterModel::flags(const QModelIndex &index) const
{
    AbstractItem* item = indexItem(index);
    if (!item || IS_ROOT(item) || IS_REFERENCE(item)) return QAbstractItemModel::flags(index);

    QModelIndex groupIdx = index;
    while (true)
    {
        if (!groupIdx.parent().isValid()) break;

        groupIdx = groupIdx.parent();
    }

    AbstractItem* groupItem = indexItem(groupIdx);
    if (!groupItem) return QAbstractItemModel::flags(index);

    QString groupId = groupItem->data(AbstractItem::IdData).toString();
    QString indexId = item->data(AbstractItem::IdData).toString();

    switch (index.column())
    {
    case Item::ValueData:
        if (groupId.endsWith(groupName(HitDie)) ||
            groupId.endsWith(groupName(SkillPoints)))
        {
            return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
        }

        if (groupId.endsWith(groupName(BaseBonuses)) ||
            groupId.endsWith(groupName(SavingThrows)))
        {
            if (groupId == indexId) return QAbstractItemModel::flags(index);
            return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
        }

        if (groupId.endsWith(groupName(ClassSkills)) ||
            groupId.endsWith(groupName(SpecialAbilities)))
        {
            if (groupId == indexId) return QAbstractItemModel::flags(index);
            return QAbstractItemModel::flags(index) | Qt::ItemIsUserCheckable;
        }
        return QAbstractItemModel::flags(index);

    case Item::NameData:
        return QAbstractItemModel::flags(index);
    default:
        return QAbstractItemModel::flags(index);
    }

}


QVariantMap D20CharacterModel::options(const QString& groupName, const QString& itemName) const
{
//    qDebug() << Q_FUNC_INFO << groupName << itemName;
    int groupId = group(groupName);

    switch (groupId)
    {
    case HitDie:        return m_campaignModel->options(D20CampaignModel::HitDiceGroup);
    case SkillPoints:   return m_campaignModel->options(D20CampaignModel::SkillPointsGroup);
    case SavingThrows:  return m_campaignModel->options(D20CampaignModel::SaveBonusGroup, itemName);
    case BaseBonuses:   return m_campaignModel->options(D20CampaignModel::BaseBonusGroup, itemName);
    }

    return QVariantMap();
}



void D20CharacterModel::initializeGroup(const int& characterGroup, const QStringList& names)
{
    QModelIndex parentIndex = groupIndex(characterGroup);
    foreach (QString name, names)
    {
        QModelIndex nameIndex = itemIndex(characterGroup, name);
        if (!nameIndex.isValid())
        {
            int row = addItem(parentIndex);
            if (row < 0) continue;

            nameIndex = index(row, AbstractItem::NameData, parentIndex);
            if (!nameIndex.isValid()) continue;

            setData(nameIndex, name, Qt::EditRole);
        }

        // todo: set buddies, when found from references...

//        QModelIndex nameIndex = nameIndex.sibling(nameIndex.row(), Item::NameData);
//        QModelIndex valueIndex = nameIndex.sibling(nameIndex.row(), Item::ValueData);


    }

    // See, that all data in the character model corresponds campaign model.
}


void D20CharacterModel::validateCheckable(const int &characterGroup, const QStringList &names)
{
    foreach (QModelIndex checkIndex, itemIndexes(characterGroup))
    {
        AbstractItem* item = indexItem(checkIndex);
        if (!item) continue;
        if (names.contains(item->data(AbstractItem::NameData).toString())) continue;

        // use partially checked data to indicate non-existent skill
        item->setData(Item::ValueData, -1);
    }
}




void D20CharacterModel::validateSelectable(const int& characterGroup, const int& campaignGroup)
{
    QModelIndexList campaignGroupIndexes = m_campaignModel->itemIndexes(campaignGroup);
    foreach (QModelIndex campaignGroupIndex, campaignGroupIndexes)
    {
        AbstractItem* campaignItem = m_campaignModel->indexItem(campaignGroupIndex);
        if (!campaignItem) continue;

        QModelIndex characterIndex = itemIndex(characterGroup, campaignItem->name());
        AbstractItem* characterItem = indexItem(characterIndex);
        if (!characterItem) continue;

        QVariant value = characterItem->data(Item::ValueData);
        if (!value.isValid()) continue;


    }



    foreach (QModelIndex checkIndex, itemIndexes(characterGroup))
    {

    }
}
